from signal import signal, SIGTERM, SIGHUP, SIGINT
from ctypes import cdll, byref, create_string_buffer

from ioloop import IOLoop
from uuid import uuid4
import log
import sys
import os


class DMQService(object):

    def __init__(self, unix_socket_dir='/tmp/dmq_sockets', uuid=None,
                 adm_worker_socket_str=""):
        """docstring for DMQService"""

        self._unix_socket_dir = unix_socket_dir

        if uuid and len(uuid) != 32:
            raise Exception(
                "DMQService::__init__, uuid supplied is of an invalid format.")

        if not os.path.exists(unix_socket_dir):
            # Create the socket directory for our unix sockets.
            os.makedirs(unix_socket_dir)

        self.uuid = uuid or uuid4().hex
        self._admin_worker_socket_str = (
            adm_worker_socket_str or "dmq_admin_%d.zmqs")
        self._znodes = []

        signal(SIGTERM, self._catch_sigterm)
        signal(SIGHUP, self._catch_sighup)
        signal(SIGINT, self._catch_sigint)

        self.admin_worker_socket_path = os.path.join(
                self._unix_socket_dir,
                self._admin_worker_socket_str % (os.getpid(),))

        self.ioloop = IOLoop.instance()
    #__init__()

    def _catch_sigterm(self, signum, frame):
        """docstring for _catch_sigterm"""
        logging.debug("DMQMaster._catch_sigterm")
        self._suicide()
    #_catch_sigterm()

    def _catch_sighup(self, signum, frame):
        """docstring for _catch_sighup"""
        logging.debug("DMQMaster._catch_sighup")
        self._suicide()
    #_catch_sighup()

    def _catch_sigint(self, signum, frame):
        """docstring for _catch_sigint"""
        logging.debug("DMQMaster._catch_sigint")
        self._suicide()
    #_catch_sigint()

    def _catch_sigquit(self, signum, frame):
        """docstring for _catch_sigquit"""
        logging.debug("DMQMaster._catch_sigquit")
    #_catch_sigquit()

    def _catch_sigstop(self, signum, frame):
        """docstring for _catch_sigstop"""
        # Do a friendly stop of the workers IOLoop,
        # but don't stop the process
        logging.debug("DMQMaster._catch_sigstop")
    #_catch_sigstop()

    def _catch_sigcont(self, signum, frame):
        """docstring for _catch_sigcont"""
        # Restart the workers IOLoop that was
        # previously stopped wth sigstop
        logging.debug("DMQMaster._catch_sigcont")
    #_catch_sigcont()

    def _suicide(self, exit_code=0):
        """docstring for _suicide"""
        logging.debug("DMQMaster._suicide")
        self.halt()
        sys.exit(exit_code)
    #_suicide()

    def _decode_admin_msg(self, msg):
        """docstring for _decode_admin_msg"""

        return uuid, headers, body
    #_decode_admin_msg()

    def _admin_app(self, msg, env):
        """docstring for _admin_rep"""
        raise Exception("DMQService::_admin_app() must be overridden")
    #_admin_app()

    def set_proc_name(self, newname):
        """
        Set the process name which is viewble from top or ps.

        ===============  ======================================================
        Parameter
        ===============  ======================================================
        *newname*        The new name of the process.
        ===============  ======================================================

        By default, a python process is usually named after
        the interpeter. Something
        like "python2.6" or just "python".
        If there is only one python process, there
        isn't a problem. However, after two or more
        python processes exits it becomes
        difficult to detemine which one is yours. So, using this function, to
        organize python process.

        *This is only known to work on Linux systems.*
        """
        logging.debug("DMQMaster.set_proc_name(newname : %s)" % (newname,))

        try:
            libc = cdll.LoadLibrary('libc.so.6')
            buff = create_string_buffer(len(newname) + 1)
            buff.value = newname
            libc.prctl(15, byref(buff), 0, 0, 0)
        except Exception as e:
            logging.warning(("set_proc_name() Unable to set "
                            "process name to %s, %s") %
                            (newname, e))
    #set_proc_name()

    def get_proc_name(self):
        """
        Get the process name from the kernel.

        ===============  ======================================================
        Parameter
        ===============  ======================================================
                            None
        ===============  ======================================================

        *This is only known to work on Linux systems.*
        """
        logging.debug("DMQMaster.get_proc_name()")

        try:
            libc = cdll.LoadLibrary('libc.so.6')
            buff = create_string_buffer(128)
            # 16 == PR_GET_NAME from <linux/prctl.h>
            libc.prctl(16, byref(buff), 0, 0, 0)
            return buff.value
        except Exception as e:
            logging.warning("set_proc_name() Unable to get process name %s" %
                (e))

        return ""

    def run(self):
        """docstring for run"""

        logging.debug("DMQService::run() starting ioloop")
        self.ioloop.start()
        logging.debug("DMQService::run() ioloop has stopped, returning.")
    #run()

    def halt(self):
        """docstring for halt"""

        for zn in self._znodes:
            logging.debug("DMQMaster.halt, closing znode %s" % (zn,))
            zn.close()

        if self.ioloop:
            self.ioloop.stop()
            self.ioloop.close(all_sockets=True)
    #halt()
#DMQService
